package com.db.williamchart.view;

import com.db.williamchart.ChartContract;
import com.db.williamchart.animation.NoAnimation;
import com.db.williamchart.data.DataPoint;
import com.db.williamchart.data.Frame;
import com.db.williamchart.data.Label;
import com.db.williamchart.data.Paddings;
import com.db.williamchart.data.configuration.ChartConfiguration;
import com.db.williamchart.data.configuration.LineChartConfiguration;
import com.db.williamchart.extensions.AttrUtils;
import com.db.williamchart.extensions.ListEx;
import com.db.williamchart.renderer.LineChartRenderer;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.BlendMode;
import ohos.agp.render.LinearShader;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.media.image.PixelMap;

import java.util.List;

public class LineChartView extends AxisChartView implements ChartContract.LineView {
    private static final float defaultSmoothFactor = 0.20f;
    private static final boolean defaultSmooth = false;
    private static final float defaultLineThickness = 4F;
    private static final int defaultFillColor = 0;
    private static final int defaultLineColor = Color.BLACK.getValue();
    private static final int defaultClickableArea = 24;

    private boolean smooth = defaultSmooth;

    private float lineThickness = defaultLineThickness;

    private int fillColor = defaultFillColor;

    private int lineColor = defaultLineColor;

    private int[] gradientFillColors = new int[]{0,0};

    private int pointsDrawableRes = -1;

    private ChartConfiguration chartConfiguration;
    public LineChartView(Context context) {
        this(context,null);
    }

    public LineChartView(Context context, AttrSet attrSet) {
        this(context, attrSet,null);
    }

    public LineChartView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(attrSet);
    }
    private void init(AttrSet attrSet){
        renderer = new LineChartRenderer(this,painter,new NoAnimation());

        lineColor = AttrUtils.getColorFromAttr(attrSet,"chart_lineColor",lineColor);
        lineThickness = (float)AttrUtils.getDimensionFromAttr(attrSet,"chart_lineThickness",(int) lineThickness);
        smooth = AttrUtils.getBooleanFromAttr(attrSet,"chart_smoothLine",smooth);
        pointsDrawableRes = AttrUtils.getIntFromAttr(attrSet,"chart_pointsDrawable",pointsDrawableRes);
    }


    @Override
    public void postInvalidate() {
        invalidate();
    }

    @Override
    public void drawLabels(List<Label> xLabels) {
        painter.prepare((float)labelsSize,labelsColor, null,null,null,null,labelsFont);
        labels.draw(canvas, painter.getPaint(), xLabels);
    }

    @Override
    public void drawGrid(Frame innerFrame, List<Float> xLabelsPositions, List<Float> yLabelsPositions) {
        grid.draw(canvas, innerFrame, xLabelsPositions, yLabelsPositions);
    }

    @Override
    public void drawDebugFrame(List<Frame> frames) {
        painter.prepare(null,-0x1000000, Paint.Style.STROKE_STYLE,null,null,null,null);
        for (Frame frame : frames){
            canvas.drawRect(frame.toRectFloat(),painter.getPaint());
        }
    }

    @Override
    public void drawLine(List<DataPoint> points) {
        Path linePath = !smooth ? ListEx.toLinePath(points) : ListEx.toSmoothLinePath(points,defaultSmoothFactor);
        painter.reset();
        painter.prepare(null,lineColor, Paint.Style.STROKE_STYLE,lineThickness,null,null,null);
        canvas.drawPath(linePath, painter.getPaint());
    }

    @Override
    public void drawLineBackground(Frame innerFrame, List<DataPoint> points) {
        Path linePath = !smooth ? ListEx.toLinePath(points) : ListEx.toSmoothLinePath(points,defaultSmoothFactor);
        Path backgroundPath = createBackgroundPath(linePath,points,innerFrame.getBottom());

        if (fillColor != 0){
            painter.prepare(null,fillColor, Paint.Style.FILL_STYLE,null,null,null,null);
        }else {
            painter.prepare(null,null, Paint.Style.FILL_STYLE,null,innerFrame.toLinearShader(gradientFillColors), Paint.ShaderType.LINEAR_SHADER,null);
        }
        painter.setBlendMode(BlendMode.DST_ATOP);
        canvas.drawPath(backgroundPath,painter.getPaint());

    }

    @Override
    public void drawPoints(List<DataPoint> points) {
        if (pointsDrawableRes != -1) {
            for (DataPoint dataPoint : points){
                Element element = getElement(pointsDrawableRes);
                centerAt(element, dataPoint.getScreenPositionX(), dataPoint.getScreenPositionY());
                element.drawToCanvas(canvas);
            }
        }
    }

    @Override
    ChartConfiguration getChartConfiguration() {
        if (chartConfiguration == null){
            chartConfiguration = new LineChartConfiguration(
                    getWidth(),
                    getHeight(),
                    new Paddings(
                            getPaddingLeft(),
                            getPaddingTop(),
                            getPaddingRight(),
                            getPaddingBottom()
                    ),
                    axis,
                    (float)labelsSize,
                    scale,
                    labelsFormatter,
                    lineThickness,
                    pointsDrawableRes != -1 ? getElement(pointsDrawableRes).getWidth() : -1,
                    pointsDrawableRes != -1 ? getElement(pointsDrawableRes).getHeight() : -1,
                    fillColor,
                    gradientFillColors,
                    AttrHelper.vp2px(defaultClickableArea,getContext())
            );
        }

        return chartConfiguration;
    }

    private Element getElement(int elementRes){
        PixelMap pixelMap = AttrUtils.getPixelMap(getContext(), elementRes);
        return new PixelMapElement(pixelMap);
    }
    private Path createBackgroundPath(
            Path path,
            List<DataPoint> points,
            float innerFrameBottom
    ){

        Path res = new Path(path);

        res.lineTo(points.get(points.size() - 1).getScreenPositionX(), innerFrameBottom);
        res.lineTo(points.get(0).getScreenPositionX(), innerFrameBottom);
        res.close();

        return res;
    }
    private void centerAt(Element element, float x, float y){
        int drawableHalfWidth = element.getWidth() / 2;
        int drawableHalfHeight = element.getHeight() / 2;

        element.setBounds((int) (x - drawableHalfWidth),
                (int) (y - drawableHalfHeight),
                (int) (x + drawableHalfWidth),
                (int) (y + drawableHalfHeight));
    }

    public void setSmooth(boolean smooth) {
        this.smooth = smooth;
    }

    public void setLineThickness(float lineThickness) {
        this.lineThickness = lineThickness;
    }

    public void setFillColor(int fillColor) {
        this.fillColor = fillColor;
    }

    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
    }

    public void setGradientFillColors(int[] gradientFillColors) {
        this.gradientFillColors = gradientFillColors;
    }
}
